ARouter 源码分析
ARouter
的源码提供两个 SDK
,分别是面向不同的阶段,本身 API
这个SDK
是面向运行期的 ,而 compiler
这个 SDK
则是作用于编译期的,我们现在分析本身 API
这个 SDK
。
初始化
首先调用的是 ARouter
的 init
方法,代码如下:
public static void init(Application application) {
if (!hasInit) { // 确保只初始化一次
hasInit = _ARouter.init(application);
if (hasInit) {
// 用于异步初始化 interceptor
_ARouter.afterInit();
}
}
ARouter
使用了门面模式,真正起作用的是 _ARouter
类。很明显,这个方法分两步处理,分别是 _ARouter.init 方法
和 _ARouter.afterInit
方法,先分析前一个。
final class _ARouter {
// 默认日志类
static ILogger logger = new DefaultLogger(Consts.TAG);
// 线程池
private volatile static ThreadPoolExecutor executor = DefaultPoolExecutor.getInstance();
private static Handler mHandler;
private static Context mContext;
private static InterceptorService interceptorService;
private _ARouter() {
}
protected static synchronized boolean init(Application application) {
mContext = application;
LogisticsCenter.init(mContext, executor);
logger.info(Consts.TAG, "ARouter init success!");
hasInit = true;
mHandler = new Handler(Looper.getMainLooper());
return true;
}
}
_ARouter
是一个单例类,里面有一个默认的线程池对象,init
方法的核心代码是 LogisticsCenter.init(mContext, executor)
,接着看这个方法:
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
mContext = context;
executor = tpe;
try {
// 通过注册表插件来加载路由表
loadRouterMap();
if (registerByPlugin) {
logger.info(TAG, "Load router map by arouter-auto-register plugin.");
} else {
Set<String> routerMap; // 保存生成类的类名集合
// 如果是 Debug 模式或新安装的版本,从 apt 生成的包中加载类
if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
// 通过指定的包名,加载由 compiler 自动生成的类
routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
if (!routerMap.isEmpty()) { // 加入 sp 缓存
context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).edit().putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply();
}
PackageUtils.updateVersion(context); // Save new version name when router map update finishes.
} else {
// 从缓存中加载
routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));
}
for (String className : routerMap) {
if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
// This one of root elements, load root.
// 加载根分组,通过反射构建实例,将根分组信息保存在 Warehouse 的 groupsIndex 集合中
((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
// Load interceptorMeta
// 加载 Interceptors 相关信息
((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceRouteMetaptorsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
// Load providerIndex
// 加载 provider 相关信息
((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
}
}
}
} catch (Exception e) {
throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");
}
}
方法步骤总结如下:
- 先判断能否通过注册表插件来加载,可以则直接通过插件来处理,否则进行第二步。
- 如果是
Debug
模式或新安装的版本,则从APT
生成的包中加载类并添加到缓存,否则直接从缓存中获取。ClassUtils.getFileNameByPackageName
方法做的就是找到app
的dex
文件 ,然后遍历出其中属于com.alibaba.android.arouter.routes
包下的所有类名,打包成集合返回。可以想象遍历整个dex
查找指定类名的工作量有多大,所以才有第一步的通过插件来加载。 - 遍历集合,反射实例化对象并调用方法,将注解生成的类的信息全部缓存到
Warehouse
类的相应变量中。之前讲注解处理器的时候有讲到,会通过APT
生成一些配置信息类,现在这些信息全部缓存到Warehouse
类中。
这里再看一下,比如分组的 Root
类,如下:
// 实现 IRouteRoot 接口
public class ARouter$$Root$$app implements IRouteRoot {
@Override
public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
// key 是分组名称,value 是对应的组的索引类
routes.put("service", ARouter$$Group$$service.class);
routes.put("test", ARouter$$Group$$test.class);
}
}
我们接着看一下 Warehouse
类:
class Warehouse {
// Cache route and metas
// Key 是根分组的名称,Value 为 对应的路由类的根类的 Class 类型
static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>();
static Map<String, RouteMeta> routes = new HashMap<>();
// Cache provider
// Key 是 provider 的全类名,Value 为 对应的路由类型
static Map<String, RouteMeta> providersIndex = new HashMap<>();
static Map<Class, IProvider> providers = new HashMap<>();
// Cache interceptor
// Key 是 interceptor 的优先级,Value 为 对应的interceptor的Class类型
static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = new UniqueKeyTreeMap<>("More than one interceptors use same priority [%s]");
static List<IInterceptor> interceptors = new ArrayList<>();
static void clear() {
routes.clear();
groupsIndex.clear();
providers.clear();
providersIndex.clear();
interceptors.clear();
interceptorsIndex.clear();
}
}
groupsIndex
对应着 Root
分组的所有配置信息,KEY
是分组名称,Value
为对应的分组索引类,索引类也由 APT
生成,管理着分组下所有的路由信息。 providersIndex
对应着 prvider
的配置信息,所有的服务类也都是 prvider
类,KEY
是服务接口的全类名,value
是一个 RouteMeta
类。 interceptorsIndex
对应着拦截器配置信息, Key
是 interceptor
的优先级,Value
为对应的 interceptor
的 Class
类型 。这跟我们讲 APT
生成的类是一样的,只不过是把所有的信息都加载到内存中罢了。
接下来,我们分析一下 _ARouter.afterInit
方法:
static void afterInit() {
// Trigger interceptor init, use byName.
interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();
}
InterceptorService
是框架提供的核心接口,这个方法是用来获取到管理拦截器的服务类(InterceptorServiceImpl)的,这个服务类在初始化时会对所有拦截器进行初始化,该方法最终会实例化 InterceptorServiceImpl
类,并调用其 init
方法进行初始化:
@Route(path = "/arouter/service/interceptor")
public class InterceptorServiceImpl implements InterceptorService {
@Override
public void init(final Context context) {
LogisticsCenter.executor.execute(new Runnable() {
@Override
public void run() {
if (MapUtils.isNotEmpty(Warehouse.interceptorsIndex)) {
for (Map.Entry<Integer, Class<? extends IInterceptor>> entry : Warehouse.interceptorsIndex.entrySet()) {
Class<? extends IInterceptor> interceptorClass = entry.getValue();
try {
// 反射构建拦截器实例
IInterceptor iInterceptor = interceptorClass.getConstructor().newInstance();
// 初始化
iInterceptor.init(context);
// 把实例添加到缓存中
Warehouse.interceptors.add(iInterceptor);
} catch (Exception ex) {
throw new HandlerException(TAG + "ARouter init interceptor error! name = [" + interceptorClass.getName() + "], reason = [" + ex.getMessage() + "]");
}
}
interceptorHasInit = true;
// 如果在进行路由时,拦截器还没有初始化完毕,会阻塞等待,初始完毕需要唤醒
synchronized (interceptorInitLock) {
interceptorInitLock.notifyAll();
}
}
}
});
}
}
该方法是在线程池中执行的,从 Warehouse
的 interceptorsIndex
中拿到所有拦截器的配置,然后遍历反射实例化对象并进行初始化,最后把实例添加到 Warehouse
的 interceptors
变量中,这个变量存储的就是所有的拦截器对象 :
//Warehouse.java
static List<IInterceptor> interceptors = new ArrayList<>();
小结:ARouter
的初始化主要是把 APT
生成的所有配置类的信息添加到内存中,并且对所有的拦截器进行了初始化,所以说拦截器的初始化时机跟 SDK 初始化时机是一样的。
路由操作
标准的路由操作如下:
ARouter.getInstance().build("/test/Test1Activity").navigation(activity);
那么我们先看一下 _ARouter
的 build
方法:
protected Postcard build(String path) {
if (TextUtils.isEmpty(path)) {
throw new HandlerException(Consts.TAG + "Parameter is invalid!");
} else {
// 预留给用户实现路径动态变换功能
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
path = pService.forString(path);
}
// 默认截取路径中的第一段作为分组名
return build(path, extractGroup(path));
}
}
首先会尝试实例化 PathReplaceService
对象,它继承 IProvider
接口,是预留给用户实现路径动态变换功能的,我们直接看最后面的代码,又调用了 build
的重载方法,第二个参数是截取的路由地址的第一段,即分组名,该方法如下:
protected Postcard build(String path, String group) {
if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group)) {
throw new HandlerException(Consts.TAG + "Parameter is invalid!");
} else {
// 这里会再次尝试动态变换路径
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
path = pService.forString(path);
}
return new Postcard(path, group);
}
}
该方法返回一个 Postcard
类,并把 path
和 group
传入构造方法中,那么我们看一下 Postcard
类:
public final class Postcard extends RouteMeta {
// Base
private Uri uri;
private Object tag; // A tag prepare for some thing wrong.
private Bundle mBundle; // 用来传输数据的
private int flags = -1; // Flags of route
private int timeout = 300; // 路由超时时间,默认 300 秒
private IProvider provider; // 如果路由目标是一个 IProvider ,会被赋值.
private boolean greenChannel; // 是否走绿色通道
private SerializationService serializationService;
// Animation
private Bundle optionsCompat; // 界面转场动画
private int enterAnim = -1;
private int exitAnim = -1;
}
Postcard
类继承于 RouteMeta
,附加了一些跳转需要用到的信息。最后调用该 Postcard
对象的 navigation
方法,层层调用,最终还是调用的 _Arouter
的 navigation
方法:
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
// 预留给用户实现的预处理服务功能
PretreatmentService pretreatmentService = ARouter.getInstance().navigation(PretreatmentService.class);
if (null != pretreatmentService && !pretreatmentService.onPretreatment(context, postcard)) {
// Pretreatment failed, navigation canceled.
return null;
}
try {
LogisticsCenter.completion(postcard);
} catch (NoRouteFoundException ex) {
if (debuggable()) {
// Show friendly tips for user.
// 在 debug 模式下,会弹 toast 友好提示
runInMainThread(new Runnable() {
@Override
public void run() {
Toast.makeText(mContext, "There's no route matched!\n" +
" Path = [" + postcard.getPath() + "]\n" +
" Group = [" + postcard.getGroup() + "]", Toast.LENGTH_LONG).show();
}
});
}
// 回调路由找不到
if (null != callback) {
callback.onLost(postcard);
} else {
// 没有回调方法,则交给全局降级策略处理,由此可见,回调优先级大于降级策略
DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
if (null != degradeService) {
// 回调降级策略的 onLost 方法
degradeService.onLost(context, postcard);
}
}
return null;
}
// 回调路由被找到
if (null != callback) {
callback.onFound(postcard);
}
// 如果不是绿色通道,即会触发拦截器,在后台线程处理,避免 ANR ,拦截器不宜处理耗时操作,不然跳转会超过导致失败
if (!postcard.isGreenChannel()) { // It must be run in async thread, maybe interceptor cost too mush time made ANR.
interceptorService.doInterceptions(postcard, new InterceptorCallback() {
@Override
public void onContinue(Postcard postcard) {
_navigation(context, postcard, requestCode, callback);
}
@Override
public void onInterrupt(Throwable exception) {
// 在子线程中回调
if (null != callback) {
callback.onInterrupt(postcard);
}
}
});
} else {
// 走绿色通道,没有拦截器,并有返回值
return _navigation(context, postcard, requestCode, callback);
}
return null;
}
方法步骤总结如下:
- 尝试实例化
PretreatmentService
对象,它继承IProvider
接口,是预留给用户实现的预处理服务功能的,如果有该接口实现类,会调用其Init
方法进行初始化,并调用预处理方法onPretreatment
。 - 调用
LogisticsCenter.completion(postcard)
方法尝试找到跳转的目标,找不到会走catch
的逻辑,即第三步,否则走第四步。 - 在
debug
模式下,会先弹toast
友好提示,有监听回调,则回调其onLost
方法,没有则会尝试寻找全局降级策略服务类,如果有该实现类,此时会回调降级策略服务类的onLost
方法,否则直接返回null
。 - 如果找到目标,有监听回调,则回调其
onFound
方法,表示路由已经找到。 - 判断是否走绿色通道,是则继续调用
_navigation
方法,否则触发拦截器。
我们先看一下 LogisticsCenter.completion(postcard)
是如何寻找目标路由的,这个方法代码量比较多,我们分两步来看:
public synchronized static void completion(Postcard postcard) {
// 从缓存集合中查找,如果没有加载过,则为null
RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
if (null == routeMeta) { // Maybe its does't exist, or didn't load.
// 找到该分组的根类
Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup()); // Load route meta.
if (null == groupMeta) {
throw new NoRouteFoundException(TAG + "There is no route match the path [" + postcard.getPath() + "], in group [" + postcard.getGroup() + "]");
} else {
try {
// 反射构建实例并添加到内存缓存中
IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
iGroupInstance.loadInto(Warehouse.routes);
// 由于已经加载实例到内存中了,则从根分组集合中移除该分组
Warehouse.groupsIndex.remove(postcard.getGroup());
} catch (Exception e) {
throw new HandlerException(TAG + "Fatal exception when loading group meta. [" + e.getMessage() + "]");
}
// 递归再次尝试加载
completion(postcard); // Reload
}
}else{
....省略后面代码
}
}
方法步骤总结如下:
- 先从缓存集合中查找
RouteMeta
对象,没有则进入if
里面的逻辑。 - 找到对应分组的索引类,索引类由
APT
生成的,保存着该分组的所有路由信息。 - 如果索引类不存在,则抛异常。
- 反射实例化该索引类,然后把所有配置信息加载到
Warehouse
的routes
集合中,该集合的KEY
是路由地址,VALUE
是RouteMeta
对象。最后,由于已经加载实例到内存中了,则从根分组集合中移除该分组,避免重复加载。 - 再次尝试加载,此时会走
else
后面的逻辑。
我们接着分析 else
后面的逻辑,代码如下:
public synchronized static void completion(Postcard postcard) {
...
// 从缓存集合中查找,如果没有加载过,则为null
RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
if (null == routeMeta) {
...
} else {
// 赋值操作
postcard.setDestination(routeMeta.getDestination());
postcard.setType(routeMeta.getType());
postcard.setPriority(routeMeta.getPriority());
postcard.setExtra(routeMeta.getExtra());
Uri rawUri = postcard.getUri();
// 如果是 Uri 跳转
if (null != rawUri) { // Try to set params into bundle.
//分割路径中的参数
Map<String, String> resultMap = TextUtils.splitQueryParameters(rawUri);
//获取Autowired注解的属性
Map<String, Integer> paramsType = routeMeta.getParamsType();
if (MapUtils.isNotEmpty(paramsType)) {
for (Map.Entry<String, Integer> params : paramsType.entrySet()) {
// 将对应的属性和值放入到 Postcard 对象的 Bundle 中
setValue(postcard,
params.getValue(),
params.getKey(),
resultMap.get(params.getKey()));
}
// 将需要自动注入的字段名称传入 Bundle
postcard.getExtras().putStringArray(ARouter.AUTO_INJECT, paramsType.keySet().toArray(new String[]{}));
}
// 保存源 uri 路径到 Bundle 中
postcard.withString(ARouter.RAW_URI, rawUri.toString());
}
switch (routeMeta.getType()) {
// 如果是 PROVIDER 类型
case PROVIDER:
Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();
IProvider instance = Warehouse.providers.get(providerMeta);
if (null == instance) { // There's no instance of this provider
IProvider provider;
try {
// 构建实例
provider = providerMeta.getConstructor().newInstance();
// 初始比
provider.init(mContext);
// 并添加到内存缓存中
Warehouse.providers.put(providerMeta, provider);
instance = provider;
} catch (Exception e) {
throw new HandlerException("Init provider failed! " + e.getMessage());
}
}
// 保存 PROVIDER 实例到 Postcard 中
postcard.setProvider(instance);
// Provider不经过拦截器处理
postcard.greenChannel(); // Provider should skip all of interceptors
break;
case FRAGMENT:
// Fragment 不经过拦截器处理
postcard.greenChannel(); // Fragment needn't interceptors
default:
break;
}
}
}
方法步骤总结如下:
- 把找到的路由目标对应的
routeMeta
类的相关参数赋值到传入的postcard
对象,此时postcard
就具备了路由的相关信息了。 - 如果是通过
Uri
的方式跳转的,则分割路径中的参数并根据Autowired
注解的属性,将对应的属性和值放入到Postcard
对象的Bundle
对象,这也就解释了为什么通过URI
的方式跳转的时候,需要在属性上面加上Autowired
注解,才能在对应的Bundle
上获取到数据。 - 如果是
PROVIDER
类型,则反射实例化对象并调用其初始化方法 ,然后添加到内存缓存中,接着保存PROVIDER
实例到Postcard
中,最后设置该Postcard
走绿色通道,因为PROVIDER
类型不需要拦截器处理。这也就解释了服务是在用到的时候才会进行初始化的。 - 如果是
FRAGMENT
类型,则设置该Postcard
走绿色通道,因为FRAGMENT
类型也不需要拦截器处理。
我们先忽略拦截器的处理,接着看 _navigation
方法,代码如下:
private Object _navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
switch (postcard.getType()) {
case ACTIVITY:
// ACTIVITY 类型,构建 Intent ,并把 postcard 的 Bundle 赋值给 Intent
final Intent intent = new Intent(currentContext, postcard.getDestination());
intent.putExtras(postcard.getExtras());
// Set flags.
int flags = postcard.getFlags();
if (-1 != flags) {
intent.setFlags(flags);
} else if (!(currentContext instanceof Activity)) { // Non activity, need less one flag.
// 没有传入 Activity ,会添加这个 flag ,最好调用的时候传入Activity
// 避免每开启一个Activity就会新开一个task,造成栈管理混乱
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
// Set Actions
String action = postcard.getAction();
if (!TextUtils.isEmpty(action)) {
intent.setAction(action);
}
// Navigation in main looper.
runInMainThread(new Runnable() {
@Override
public void run() {
// 如果不在主线程,则切换到主线程执行
startActivity(requestCode, currentContext, intent, postcard, callback);
}
});
break;
case PROVIDER:
// 直接返回
return postcard.getProvider();
case BOARDCAST:
case CONTENT_PROVIDER:
case FRAGMENT:
Class fragmentMeta = postcard.getDestination();
try {
// 反射构建 Fragment 实例,并调用 setArguments 把 Bundle 传入
Object instance = fragmentMeta.getConstructor().newInstance();
if (instance instanceof Fragment) {
((Fragment) instance).setArguments(postcard.getExtras());
} else if (instance instanceof android.support.v4.app.Fragment) {
((android.support.v4.app.Fragment) instance).setArguments(postcard.getExtras());
}
return instance;
} catch (Exception ex) {
logger.error(Consts.TAG, "Fetch fragment instance error, " + TextUtils.formatStackTrace(ex.getStackTrace()));
}
case METHOD:
case SERVICE:
default:
return null;
}
// Activity 类型是返回 Null 的,拿不到实例
return null;
}
方法步骤总结如下:
ACTIVITY
类型,构建Intent
并把postcard
的Bundle
赋值给Intent
,然后设置Intent
的flags
和actions
,如果不在主线程,会先切换到主线程进行界面跳转。(因为拦截器运行在子线程,里面也会回调这个方法,所以需要线程切换)。PROVIDER
类型,直接返回之前设置到postcard
的provider
对象即可。FRAGMENT
类型,反射实例化Fragment
对象并调用其setArguments
方法把Bundle
传入,然后返回该实例。
注意:如果路由的时候没有传入 Activity
对象,但是跳转类型是 Activity
类型,则会调用 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
代码,这会导致每启动一个 Activity
就会新开一个 task
,所以建议普通界面跳转都传入 Activity
,避免栈的混乱。
拦截过程分析
在分析拦截器的过程之前,我们先了解一下 CountDownLatch
类,简单来说 CountDownLatch
可以阻塞一个线程,直到内部计数器为 0 时才继续执行阻塞的线程,计数器的初始值通过构造传入,通过调用 countDown()
方法减少一个计数。 CancelableCountDownLatch
类继承自 CountDownLatch
,并增加了 cancel
方法,用于直接将计数归 0 ,放开阻塞:
public void cancel() {
// 循环调用 countDown 方法,直到 count 为 0,放开阻塞
while (getCount() > 0) {
countDown();
}
}
现在正式来分析拦截过程,拦截功能是通过 ARouter
提供的 interceptorService
实现的,我们在初始化分析时已经提到过,接下来看看具体是如何拦截的:
interceptorService.doInterceptions(postcard, new InterceptorCallback() {
@Override
public void onContinue(Postcard postcard) {
// 继续路由跳转
_navigation(context, postcard, requestCode, callback);
}
@Override
public void onInterrupt(Throwable exception) {
// 在子线程中回调
if (null != callback) {
callback.onInterrupt(postcard);
}
}
});
该方法将会在子线程中运行,所以需要回调的方式来处理结果,onContinue
方法会接着走我们已经分析的路由跳转逻辑,而 onInterrupt
方法会回调到路由跳转时传入的监听回调的 onInterrupt
方法。我们接着分析 doInterceptions
方法:
// InterceptorServiceImpl.java
public void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {
// 如果有拦截器
if (null != Warehouse.interceptors && Warehouse.interceptors.size() > 0) {
// 检查拦截器的初始化状态
checkInterceptorsInitStatus();
if (!interceptorHasInit) {
callback.onInterrupt(new HandlerException("Interceptors initialization takes too much time."));
return;
}
LogisticsCenter.executor.execute(new Runnable() {
@Override
public void run() {
// 构建 CancelableCountDownLatch ,初始计数为拦截器的数量
CancelableCountDownLatch interceptorCounter = new CancelableCountDownLatch(Warehouse.interceptors.size());
try {
_excute(0, interceptorCounter, postcard);
// 阻塞线程直到超时,或者计数归0
// 超时时间默认为 300 秒
interceptorCounter.await(postcard.getTimeout(), TimeUnit.SECONDS);
if (interceptorCounter.getCount() > 0) { // Cancel the navigation this time, if it hasn't return anythings.
// 拦截超时
callback.onInterrupt(new HandlerException("The interceptor processing timed out."));
} else if (null != postcard.getTag()) { // Maybe some exception in the tag.
// 被拦截
callback.onInterrupt(new HandlerException(postcard.getTag().toString()));
} else {
// 放行
callback.onContinue(postcard);
}
} catch (Exception e) {
callback.onInterrupt(e);
}
}
});
} else {
callback.onContinue(postcard);
}
}
private static void _excute(final int index, final CancelableCountDownLatch counter, final Postcard postcard) {
//如果不是最后一个拦截器
if (index < Warehouse.interceptors.size()) {
// 索引从0开始,即从第一个拦截器开始获取
IInterceptor iInterceptor = Warehouse.interceptors.get(index);
iInterceptor.process(postcard, new InterceptorCallback() {
@Override
public void onContinue(Postcard postcard) {
// Last interceptor excute over with no exception.
// 计数减1,放行到下一个拦截器处理
counter.countDown();
_excute(index + 1, counter, postcard);
}
@Override
public void onInterrupt(Throwable exception) {
// 拦截,将 Exception 传入到 postcard 的 tag 字段,
postcard.setTag(null == exception ? new HandlerException("No message.") : exception.getMessage()); // save the exception message for backup.
// 计数归0
counter.cancel();
}
});
}
}
该方法首先判断是否存在拦截器,没有则直接回调 onContinue
方法,有则会检查拦截器的初始化状态:
private static void checkInterceptorsInitStatus() {
synchronized (interceptorInitLock) {
while (!interceptorHasInit) {
try {
// 等待10秒钟
interceptorInitLock.wait(10 * 1000);
} catch (InterruptedException e) {
throw new HandlerException(TAG + "Interceptor init cost too much time error! reason = [" + e.getMessage() + "]");
}
}
}
}
方法很简单,判断 interceptorHasInit
是否为 ture
,否则等待 10
秒让拦截器进行初始化,初始化成功后会将 interceptorHasInit
设置为 true
,否则会抛异常。因此,拦截器不宜太多,而且不要在拦截器里面做耗时操作,不然跳转响应会很慢。
接着会通过线程池执行一个 Runnable
对象,然后会创建一个与拦截器数量相同的 CancelableCountDownLatch
初始计数值,每放行一个拦截器就 countDown
,并交给后一个拦截器处理,如果拦截了则直接归 0 计数,并将拦截的 Throwable
存入 postcard
的 tag
字段,interceptorCounter.await()
会阻塞直到计数归 0 或者阻塞超时(默认是300秒),最后通过 interceptorCounter.getCount()
判断是否是超时,还是拦截或者放行。
注意:拦截的过程都是在子线程中处理,包括 Interceptor 的 process 也是在子线程调用的,因此,如果想要在拦截过程中展示 dialog 等操作都需要切换到主线程。
数据注入
到此,跳转逻辑已全部分析完毕,我们接着分析一下数据注入的逻辑。我们知道自动注入功能需要实现如下代码:
ARouter.getInstance().inject(this);
最终会调用 _ARouter
的 inject
方法:
static void inject(Object thiz) {
// 获取框架内的注入服务实例
AutowiredService autowiredService = ((AutowiredService) ARouter.getInstance().build("/arouter/service/autowired").navigation());
if (null != autowiredService) {
// 调用 autiowire 方法
autowiredService.autowire(thiz);
}
}
AutowiredService
跟之前的降级服务和拦截器服务一样,都是框架提供的核心接口,我们看一下它的实现类:
@Route(path = "/arouter/service/autowired")
public class AutowiredServiceImpl implements AutowiredService {
private LruCache<String, ISyringe> classCache;
private List<String> blackList;
@Override
public void init(Context context) {
classCache = new LruCache<>(66);
blackList = new ArrayList<>();
}
@Override
public void autowire(Object instance) {
String className = instance.getClass().getName();
try {
if (!blackList.contains(className)) {
ISyringe autowiredHelper = classCache.get(className);
// 如果没有缓存
if (null == autowiredHelper) { // No cache.
//则构建一个注入辅助类,比如要注入的Activity 是 Test3Activity ,则这里获取的就是
// Test3Activity$$ARouter$$Autowired,这是通过 apt 自动生成的
autowiredHelper = (ISyringe) Class.forName(instance.getClass().getName() + SUFFIX_AUTOWIRED).getConstructor().newInstance();
}
autowiredHelper.inject(instance);
classCache.put(className, autowiredHelper);
}
} catch (Exception ex) {
blackList.add(className); // This instance need not autowired.
}
}
}
方法很清晰,用到了 LruCache
来做缓存处理,然后获取之前通过 APT
生成的注入辅助类,并调用其 inject
方法,这一部分我们之前已经分析过了,这里再看一下辅助类的代码:
public class Test3Activity$$ARouter$$Autowired implements ISyringe {
private SerializationService serializationService;
@Override
public void inject(Object target) {
serializationService = ARouter.getInstance().navigation(SerializationService.class);
Test3Activity substitute = (Test3Activity)target;
substitute.name = substitute.getIntent().getStringExtra("teacherName");
substitute.age = substitute.getIntent().getIntExtra("age", substitute.age);
substitute.testPac = substitute.getIntent().getParcelableExtra("testPac");
if (null != serializationService) {
substitute.testObj = serializationService.parseObject(substitute.getIntent().getStringExtra("testObj"), new com.alibaba.android.arouter.facade.model.TypeWrapper<TestObj>(){}.getType());
} else {
Log.e("ARouter::", "You want automatic inject the field 'testObj' in class 'Test3Activity' , then you should implement 'SerializationService' to support object auto inject!");
}
}
}
注意:这里赋值的操作是直接调用“目标类对象.属性”的方式赋值,因此, private
修饰的属性无法通过这种方式赋值,并且在赋值时会抛出异常,被 AutowiredServiceImpl
的 autowire
方法中的 try-catch
捕获,存入不需要注入的集合中,最终导致同一个类中的其他非 private
属性也无法注入。